<?php

/**
 * PageTree是一个页面的组成配置，它是一种树状结构的数据，每个节点都代表页面上的某个元素或者配置信息
 * 
 * @author YcoeXu
 *
 */
class PageTree {
	/**
	 * 整棵树
	 * @var array
	 */
	private $tree = null;
	
	/**
	 * 树的索引，可以通过索引节点ID快速找到树内的任意一个节点，它保存的是ID=>节点地址
	 * @var array
	 */
	private $tree_indexs = array();
	
	/**
	 * 数据库操作类
	 * @var PageModel
	 */
	private $page_mod = null;
	
	/**
	 * YuSmarty对象
	 * @var YuSmarty
	 */
	private $smarty = null;
	
	/**
	 * 根据此页面配置生成的模板字符串
	 * @var unknown_type
	 */
	private $html = null;
	
	/**
	 * 样式代码
	 * @var string
	 */
	private $styles = '';
	
	/**
	 * 构造函数
	 * @param $id 树ID，即数据库中的_id
	 */
	public function PageTree($id=null, $mod=null){
		if(!defined('WIDGET_DIR')){
			define('WIDGET_DIR', ROOT_PATH . '/protected/includes/widgets/');
		}
		if($mod === null){
			$this->page_mod = new PageModel();
		}else{
			$this->page_mod = $mod;
		}
		
		if(is_string($id)){
			//尝试从数据库中获取
			$this->tree = $this->page_mod->get_by_key_site_limit($id);
		}elseif(is_array($id)){
			$this->tree = $id;
		}
		
		//初始化索引
		if(!empty($this->tree)){
			self::build_indexs($this->tree['root']);
		}
	}
	
	public function get_item($item_id){
		if($item_id === 'root'){
			$item_id = $this->tree['root']['_id'];
		}
		if(array_key_exists($item_id, $this->tree_indexs)){
			return $this->tree_indexs[$item_id];
		}
		return null;
	}
	
	/**
	 * 通过名字初始化，一般是初始化已经发布状态的页面
	 * @param string $name
	 */
	public function init_page_by_name($name){
		$condition = array('site_id'=>SITE_ID, 'name'=>$name, 'available'=>false);
		$this->tree = $this->page_mod->get_by_attrs($condition);

		//初始化索引
		if(!empty($this->tree)){
			self::build_indexs($this->tree['root']);
		}
	}
	
	/**
	 * 生成Smarty模板文件
	 */
	public function gen_template(){
		if($this->html!==null)
			return $this->html;
		if($this->smarty === null){
			$this->smarty = Viewer::get_view();
			$this->smarty->left_delimiter = '#{';//设置一级模板的左边符
		}
		
		//要生成的模板名称
		$template_name = $this->tree['name'] . '_' . $this->tree['_id'] . '.' . TEMPLATE_TYPE;
		$html = self::gen_node_template($this->tree['root']);
		
		// <!-- holder:css -->
		$css_path = self::css_merge();
		$html = preg_replace('/<\!--\s*holder:\s*css\s*-->/i', '<link rel="stylesheet" href="' . CACHE_URL .$css_path.'" type="text/css"/>', $html);
		
		// <!-- holder:scripts -->
		
		return $html;
	}
	
	/**
	 * 保存进数据库
	 */
	public function save(){
		$this->tree['update'] = time();
		if($this->page_mod->exists_key_site_limit($this->tree['_id'])){
			//修改
			//清除旧文件
			$css_file_path = self::get_css_path($this->tree['_id']);
			$css_file_path = USER_TEMPLATE_PATH . 'css/' . $css_file_path;
			if(is_file($css_file_path)){
				unlink($css_file_path);
			}
			$this->page_mod->update_by_key_site_limit($this->tree['_id'], $this->tree);
		}else{
			//新增
			$this->page_mod->insert($this->tree);
		}
		//生成模板文件
		$this->html = self::gen_template();
		if(empty($this->html)){
			return self::error('保存模板文件失败！');
		}
		
		//保存模板
		$template_path = USER_TEMPLATE_PATH . $this->tree['agent'] . '/';
		if(!file_exists($template_path)){
			yu_mkdirs($template_path);
		}
		if($this->tree['available'] === true){
			//如果是正在版
			$template_file_name = $this->tree['name'] . '.' . TEMPLATE_TYPE;
		}else{
			//测试版时
			$template_file_name = $this->tree['name'] . '_' . $this->tree['_id'] . '.' . TEMPLATE_TYPE;
		}
		file_put_contents($template_path . $template_file_name, $this->html);
		return self::success('保存成功！');
	}
	
	/**
	 * 样式文件名称
	 * @param $id
	 */
	private function get_css_path($id){
		return $this->tree['name'] . '_' . $id . '.css';
	}
	
	/**
	 * 重新配置
	 * @param $node
	 */
// 	public function set_node($node){
// 		if(!array_key_exists($node['id'], $this->tree_indexs)){
// 			return self::error('要配置的节点找不到！');
// 		}
// 		$this->tree_indexs['id'] = $node;
// 		return self::success('修改成功！');
// 	}
	
	public function add_style($node_id, $styles){
		if(!array_key_exists($node_id, $this->tree_indexs)){
			return self::error('要配置的节点找不到！');
		}
		$node = &$this->tree_indexs[$node_id];
		if(isset($node['styles'])){
			//合并
			$node['styles'] = array_merge($node['styles'], $styles);
		}else{
			$node['styles'] = $styles;
		}
	}
	
	/**
	 * 将节点移到新的节点下，移动节点将会同时移动节点的子节点
	 * @param string $node_id
	 * @param string $new_parent_id
	 */
	public function move($node_id, $new_parent_id=null){
		if(!array_key_exists($node_id, $this->tree_indexs)){
			return self::error('要迁移的节点找不到！');
		}
		if($new_parent_id!==null && !array_key_exists($new_parent_id, $this->tree_indexs)){
			return self::error('要迁移进的节点找不到！');
		}
		$node = &$this->tree_indexs[$node_id];
		
		//从原父节点上移除
		if(isset($node['parent_id'])){
			$old_parent_id = $node['parent_id'];
			$parent_node = &$this->tree_indexs[$old_parent_id];
		}else{
			//根节点上
			$parent_node = &$this->tree['root'];
		}
		foreach ($parent_node['items'] as $holper => $sub_nodes){
			foreach($sub_nodes as $index=>$sub_node){
				if($sub_node['id'] == $node_id){
					unset($parent_node['items'][$holper][$index]);
				}
			}
		}
		
		if($new_parent_id==null){
			unset($node['parent_id']);//设置为根节点上
			$parent_node = &$this->tree['root']['items'];
		}else{
			$node['parent_id'] = $new_parent_id;
			$parent_node = &$this->tree_indexs[$new_parent_id]['items'];
		}
		$parent_node[] = $node;
		//重新排序
		sort_by_rank($parent_node, false);
		return self::success('移动成功！');
	}
	
	/**
	 * 删除某个节点，删除此节点会将本节点下的所有节点都一并删除！
	 * @param $node_id 要删除的节点ID
	 */
	public function remove_node($node_id){
		if(!array_key_exists($node_id, $this->tree_indexs)){
			return self::error('要删除的节点找不到！');
		}
		$node = &$this->tree_indexs[$node_id];
		
		//从原父节点上移除
		if(isset($node['parent_id'])){
			$old_parent_id = $node['parent_id'];
			$parent_node = &$this->tree_indexs[$old_parent_id];
		}else{
			//根节点上
			$parent_node = &$this->tree['root'];
		}
		foreach ($parent_node['items'] as $holper => $sub_nodes){
			foreach($sub_nodes as $index=>$sub_node){
				if($sub_node['id'] == $node_id){
					unset($parent_node['items'][$holper][$index]);
				}
			}
		}
		reset($nodes);
		return self::success('删除成功！');
	}
	
	/**
	 * 向某个节点上添加一个节点
	 * @param array $node
	 * @param string $parent_id，如果为空，则表示添加到根节点上
	 */
	public function add_node($node, $parent_id=null, $contner=null){
		if($parent_id === null || $parent_id === 'root'){
			//添加到根节点
			$this->tree['root']['items'][$contner][] = $node;
		}else{
			$this->tree_indexs[$parent_id]['items'][$contner][] = $node;
		}
		$this->tree_indexs[$node['id']] = $node;
	}
	
	/**
	 * 添加一个样式文件
	 * @param string $css_file
	 */
	public function add_css($css_file){
		if(!in_array($css_file, $this->tree['csses'])){
			$this->tree['csses'][] = $css_file;
		}
	}
	
	/**
	 * 添加一个脚本文件
	 * @param string $js_path
	 */
	public function add_js($js_path){
		if(!in_array($js_path, $this->tree['javascripts'])){
			$this->tree['javascripts'][] = $js_path;
		}
	}
	
	public function gen_node_template($node, $holder=null){
		$name = $node['name'];
		$type = $node['type'];
		
		$is_layout = ($type == 'layout' || $type=='root');
		switch ($type){
			case 'root':
			case 'layout':
				//如果是layout或者root时，直接将HTML放出来
				return self::gen_layout_template($node, $holder);
				break;
			case 'cell':
				return self::gen_cell_template($node, $holder);
				break;
			case 'widget':
				return self::gen_widget_template($node, $holder);
				break;
			default:
				return '未知类型节点！';
		}
	}
	
	/**
	 * 获取挂件的模板，直接生成挂件调用的方式
	 * @param unknown_type $node
	 * @param unknown_type $holder
	 */
	private function gen_widget_template($node, $holder=null){
		return SMARTY_LEFT_DELIMITER . 'widget id="' . $node['id'] . '"' . SMARTY_RIGHT_DELIMITER;
	}
	
	private function gen_cell_template($node, $holder=null){
		//获取元件
		$cell_name = $node['name'];
		$cell_id = $node['id'];
		//从数据库中取
		$cell_mod = new CellModel();
		$cell = $cell_mod->get_by_key($cell_id);
		if(empty($cell)){
			return '元件不存在：cell_id=' . $cell_id;
		}
		$html = '';
		foreach ($cell['items'] as $node){
			$html .= self::gen_node_template($node, $holder);
		}
		return $html;
	}
	
	private function gen_layout_template($node, $holder=null){
		//获取模板路径
		$template_path = USER_TEMPLATE_PATH . 'data/widget_template/' . $node['cate'] . '/' .$node['name'] . '/template/' . $node['template'] . '.' . TEMPLATE_TYPE;
		if(!is_file($template_path)){
			$template_path = WIDGET_DIR . $node['cate'] . '/' . $node['name'] . '/template/' . $node['template'] . '.' . TEMPLATE_TYPE;
			
			if(!is_file($template_path)){
				return '节点[id:' . $node['id'] . ', type:' . $node['type'] . ', name:' . $node['name'] . ', template:' . $node['template'] . ']的模板找不到！' . $template_path;
			}
		}
		$template = file_get_contents($template_path);
		
		if(isset($node['config'])){
			$configs = $node['config'];
		}else{
			$configs = array();
		}
		$configs['id'] = $node['id'];
		
		//抽取样式
		self::get_style_string($node);
		
		//先执行一级编译
		$this->smarty->assign($configs);
		$template = $this->smarty->compile($template);
		$this->smarty->clearAllAssign(); //清除之前的旧数据
		if(isset($node['items'])){
			foreach ($node['items'] as $holder_id=>$items){
				$html = '';
				foreach ($items as $item){
					$html .= self::gen_node_template($item, $holder_id);
				}
				//替换占位符
				$template = preg_replace('/<\!--\s*holder:\s*' . $holder_id . '\s*-->/i', $html, $template);
			}
		}
		//清空占位符
		$template = preg_replace('/<\!--\s*holder:\s*body.*\s*-->/i', '', $template);
		return $template;
	}
	
	/**
	 * 将各个节点下的样式抽取到PageTree::styles下
	 * @param array $node
	 */
	private function get_style_string($node){
		if(isset($node['styles'])){
			foreach ($node['styles'] as $selector=>$style_list){
				$this->styles .= "\n".$selector . '{';
				foreach ($style_list as $style_name => $style_value){
					$this->styles .= $style_name . ':' . $style_value.';';
				}
				$this->styles .= '}';
			}
		}
	}
	
	/**
	 * 合并样式表并写入到样式文件中
	 * 如果是正式发布版本时，将压缩样式文件
	 * 返回可以直接访问的链接
	 */
	public function css_merge(){
		$csses = $this->tree['csses'];
		$css_str = '';
		foreach ($csses as $css){
			if($css === '$base_css'){
				//使用基本样式
				$css = '/template/css/base.css';
			}
			$css_path = ROOT_PATH . $css;
			if(is_file($css_path)){
				$css_str .= file_get_contents($css_path);
			}
		}
		if($this->tree['available'] == true){
			//只有对要发布的页面进行压缩处理
			$css_str = preg_replace(array(
				'%/\*.*?\*/%s',
				'/\n|\t|\s{2,}/s',
			), '', $css_str);
			$css_str = preg_replace('/\s*:\s+/s', ':', $css_str);
			$css_str = preg_replace('/;\s*\}/s', '}', $css_str);
		}
		
		$css_str .= $this->styles;
		
		//保存进新文件中
		$new_css_file = self::get_css_path($this->tree['_id']);
		$css_path = USER_TEMPLATE_PATH . $this->tree['agent'] . '/css/';
		if(!file_exists($css_path)){
			yu_mkdirs($css_path);
		}
		file_put_contents($css_path . $new_css_file, $css_str);
		return '/template/' . $this->tree['agent'] . '/css/' . $new_css_file;
	}
	
	/**
	 * 创建一棵新树
	 * @param string $name 模板名称，比如：site_index | goods_detail等
	 * @param string $module 编辑类型，比如：web | wap，默认为web
	 * @param string $desc
	 */
	public function create($name, $agent=null, $desc=null){
		if($agent === null)
			$agent = 'web';
		$this->tree = array(
			'_id' => gen_guid(),
			'site_id' => SITE_ID,
			'desc' => $desc,
			'agent' => $agent,
			'name' => $name,
			'available' => false,
			//样式文件列表
			'csses' => array(
				'$base_css', //引入一个最基本的样式表
			),
			//脚本文件列表
			'javascripts' => array(
				'$jquery',	//引用jquery库
			),
			'root'=>array(
				'id' => 'B3475B60CE8441B7AD971161BC33B6AD',
				'name' => 'root',
				'type' => 'root',
				'template' => $agent,	//模块类型，可以是：web|wap
				'cate' => 'layout',
				'items' => array(	//子项
					'body'=>array(	//body占位符下的的元素
						array(
							'id' => gen_guid(),
							'name' => 'layout',
							'template'=>'layout_1',
							'type'=>'layout', 
							'rank'=>1000,
							'cate' => 'layout',
							'config' => array(
								'id1' => gen_guid(),
							),
							'items'=>array(
							)
						),
						array(
							'id' => gen_guid(),
							'name' => 'layout',
							'template'=>'layout_2', 
							'cate' => 'layout',
							'config' => array(
								'id1' => gen_guid(),
								'id2' => gen_guid(),
							),
							'type'=>'layout',
							'rank'=> 1000,
							'items'=>array(
								'body2'=>array(
									array(
										'id' => gen_guid(),
										'name' => 'layout',
										'template'=>'layout_2',
										'cate' => 'layout',
										'config' => array(
											'id1' => gen_guid(),
											'id2' => gen_guid(),
										),
										'type'=>'layout',
										'rank'=> 1000,
									),
								),
							),
						),
					),
					'footer_code' => array(
						
					),
				),
			),
		);
	}
	
	/**
	 * 重建索引
	 * @param array $nodes 树的某个节点
	 * @param string $parent_id 父节点ID
	 */
	private function build_indexs(&$node, $parent_id=null){
		if(empty($node)){
			return;
		}
		if($parent_id!==null){
			$node['parent_id'] = $parent_id;
		}
		$this->tree_indexs[$node['id']] = &$node;
		if(isset($node['items'])){
			foreach ($node['items'] as $contner_id=>&$contner){
				foreach ($contner as &$sub_node){
					self::build_indexs($sub_node, $node['id']);
				}
			}
		}
	}
	
	/**
	 * 返回错误信息
	 * @param string $info
	 */
	private function error($info){
		return array('result'=>false, 'info'=>$info);
	}
	
	private function success($info){
		return array('result'=>true, 'info'=>$info);
	}
}

?>